#pragma once

#include "data.hpp"
#include "httplib.h"

extern cloud::DataManager *_data;

namespace cloud
{
    class Service
    {
    private:
        int _server_port;
        string _server_ip;
        string _download_prefix;
        httplib::Server _server;
        static void Upload(const httplib::Request &req, httplib::Response &res)
        {
            bool ret = req.has_file("file");
            if (ret == false)
            {
                res.status = 400;
                return;
            }
            const auto &file = req.get_file_value("file");
            string back_dir = Config::GetInstance()->GetBackDir();
            string realpath = back_dir + Util::FileUtil(file.filename).FileName();
            Util::FileUtil fu(realpath);
            fu.SetContent(file.content);
            BackupInfo info;
            info.NewBackupInfo(realpath);
            _data->Insert(info);
            
        }

        static string TimetoStr(time_t tm)
        {
            string tem = ctime(&tm);
            return tem;
        }

        static void ListShow(const httplib::Request &req, httplib::Response &res)
        {
            vector<BackupInfo> arr;
            _data->GetAll(&arr);

            stringstream ss;
            ss << "<html><head> <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";

            for (auto &a : arr)
            {
                ss << "<tr>";
                string filename = Util::FileUtil(a.real_path).FileName();
                ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>";
                ss << "<td align='right'>" << TimetoStr(a.mtime) << "</td>";
                ss << "<td align='right'>" << a.fsize / 1024 << "k</td>";
                ss << "</tr>";
            }
            ss<<"</table></body></html>";
            res.body = ss.str();
            res.set_header("Content-Type","text/html");
            res.status = 200;
        }
        static string GetETag(const BackupInfo& info)
        {
            Util::FileUtil fu(info.real_path);
            string etag = fu.FileName();
            etag+="-";
            etag+=to_string(info.fsize);
            etag+="-";
            etag+=to_string(info.mtime);
            return etag;
        }
        static void Download(const httplib::Request &req, httplib::Response &res)
        {
            BackupInfo info;

            _data->GetOneByURL(req.path,&info);

            if(info.pack_flag ==true)
            {
                Util::FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path);//将文件解压到备份目录下
                fu.Remove();
                info.pack_flag = false;
                _data->Update(info);
            }

            bool retrans = false;
            string old_etag;
            if(req.has_header("If-Range"))
            {
                old_etag = req.get_header_value("If-Range");
                if(old_etag==GetETag(info))
                {
                    retrans = true;
                }
            }
            Util::FileUtil fu(info.real_path);
            if(retrans==false)
            {
                fu.GetContent(&res.body);
                res.set_header("Accpet-Ranges","bytes");
                res.set_header("ETag",GetETag(info));
                res.set_header("Content-Type", "application/octet-stream");
                res.status = 200;
            }else{
                //由于断点续传httplib头文件已经帮我们判断好了，是否有If-Range,ETag是否发生改变等，所以
                //httplib会自动帮我们截取区间数据响应给客户端，状态码也会自动设置
                fu.GetContent(&res.body);
                res.set_header("Accpet-Ranges","bytes");
                res.set_header("ETag",GetETag(info));
                res.set_header("Content-Type", "application/octet-stream");
                res.status = 206;
            }

        }

    public:
        Service()
        {
            Config *config = Config::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
        }
        bool RunModule()
        {
            _server.Post("/upload", Upload);
            _server.Get("/listshow", ListShow);
            _server.Get("/", ListShow);
            string download_url = _download_prefix + "(.*)";
            _server.Get(download_url, Download);
            _server.listen(_server_ip.c_str(), _server_port);
            return true;
        }
    };
}